package download

import (
	"fmt"
	tp "gitee.com/swsk33/concurrent-task-pool/v2"
	"gitee.com/swsk33/gopher-fetch"
	"gitee.com/swsk33/landsat-fetch/api"
	"gitee.com/swsk33/landsat-fetch/global"
	"gitee.com/swsk33/landsat-fetch/model"
	"gitee.com/swsk33/landsat-fetch/util"
	"gitee.com/swsk33/sclog"
	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"os"
	"path/filepath"
	"time"
)

// 任务记录文件
const taskFilePath = "task.json"

// 根据配置文件中配置的下载列表，获取对应列表中全部下载地址
func requestAllUrl() ([]*model.GranuleTask, error) {
	result := make([]*model.GranuleTask, 0)
	for _, name := range global.TotalConfig.Download.Labels {
		sclog.Info("正在读取下载列表：%s...\n", name)
		// 先加载列表任务
		_, e := api.LoadDownloadList(name)
		if e != nil {
			sclog.Error("加载下载任务列表%s出错！\n", name)
			return nil, e
		}
		// 然后获取下载链接
		list, e := api.GetDownloadUrlList(name)
		if e != nil {
			sclog.Error("获取%s的下载内容出错！\n", name)
			return nil, e
		}
		result = append(result, list...)
	}
	sclog.Info("已获取全部下载影像地址！一共：%d个影像！\n", len(result))
	return result, nil
}

// 更新任务列表的url
//
// origin 原始任务列表
// newList 新请求到的任务列表
//
// 最终会根据origin的identifier到newList中获取url，最后生成一个新的下载链接列表
func updateUrl(origin, newList []*model.GranuleTask) []*model.GranuleTask {
	// 转换成map
	originMap := make(map[string]*model.GranuleTask)
	for _, item := range origin {
		originMap[item.Identifier] = item
	}
	newMap := make(map[string]string)
	for _, item := range newList {
		newMap[item.Identifier] = item.Url
	}
	// 根据原始的identifier去新的列表获取url
	result := make([]*model.GranuleTask, 0)
	for id := range originMap {
		url, ok := newMap[id]
		if ok {
			result = append(result, model.NewGranuleTask(id, url, originMap[id].Label))
		}
	}
	return result
}

// 开始执行下载任务的子命令
var executeCmd = &cobra.Command{
	Use:   "execute",
	Short: "执行下载任务",
	Long:  "根据配置文件中指定的labels列表，获取并下载对应下载任务列表中全部遥感影像，并保存至配置文件指定的目录中",
	Run: func(cmd *cobra.Command, args []string) {
		// 登录
		e := api.Login(global.TotalConfig.Auth.Username, global.TotalConfig.Auth.Token)
		if e != nil {
			sclog.ErrorLine(e.Error())
			return
		}
		// 根据任务文件情况，判断是否恢复任务文件或者新建任务列表
		var totalTaskList []*model.GranuleTask
		if util.FileExists(taskFilePath) {
			sclog.InfoLine("任务进度文件存在！将从文件恢复进度...")
			// 读取进度
			totalTaskList, e = tp.LoadTaskFile[*model.GranuleTask](taskFilePath)
			if e != nil {
				sclog.ErrorLine(e.Error())
				return
			}
			// 更新url
			sclog.InfoLine("更新下载链接...")
			newList, e := requestAllUrl()
			if e != nil {
				sclog.ErrorLine(e.Error())
				return
			}
			totalTaskList = updateUrl(totalTaskList, newList)
		} else {
			sclog.InfoLine("任务进度文件不存在！将新建下载任务...")
			totalTaskList, e = requestAllUrl()
			if e != nil {
				sclog.ErrorLine(e.Error())
				return
			}
		}
		// 用于下载时的日志输出器
		realTimeLogger := util.NewRealTimeStatusLogger(color.FgHiBlue)
		// 阶段状态日志打印器，共享锁
		logger := sclog.NewMutexLoggerShareLock(&realTimeLogger.Lock)
		// 创建并发任务池，准备下载
		taskPool := tp.NewNoDelayTaskPool[*model.GranuleTask](global.TotalConfig.Download.ParallelCount, totalTaskList,
			// 下载任务的逻辑
			func(granuleTask *model.GranuleTask, pool *tp.TaskPool[*model.GranuleTask]) {
				// 计算下载位置
				filePath := filepath.Join(global.TotalConfig.Download.Folder, granuleTask.Label, fmt.Sprintf("%s.tar", granuleTask.Identifier))
				// 创建目标文件夹
				e := util.MkdirIfNotExists(filepath.Dir(filePath))
				if e != nil {
					logger.ErrorLine("创建下载文件夹失败！")
					logger.ErrorLine(e.Error())
					pool.Retry(granuleTask)
					return
				}
				// 创建下载任务
				fetchTask := gopher_fetch.NewParallelGetTask(
					granuleTask.Url,
					filePath,
					"",
					100*time.Millisecond,
					global.TotalConfig.Download.Concurrent,
				)
				// 注册监听，在其中将下载状态注册到状态列表
				fetchTask.SubscribeStatus(func(status *gopher_fetch.TaskStatus) {
					realTimeLogger.StatusMap.Store(granuleTask.Identifier, status)
				})
				// 开始下载
				logger.Info("正在下载：%s...\033[K\n", granuleTask.Identifier)
				e = fetchTask.Run()
				// 完成后，从状态列表删除
				realTimeLogger.StatusMap.Delete(granuleTask.Identifier)
				if e != nil {
					logger.Error("下载%s出现错误：%s，将稍后重试...\033[K\n", granuleTask.Identifier, e)
					pool.Retry(granuleTask)
					return
				}
				logger.Info("影像：%s下载完成！\033[K\n", granuleTask.Identifier)
			},
			// 停机信号处理逻辑
			func(pool *tp.TaskPool[*model.GranuleTask]) {
				// 保存任务
				logger.Warn("接收到中断信号，即将完成最后的进度保存工作...\033[K\n")
				e := pool.SaveTaskList(taskFilePath)
				if e != nil {
					logger.ErrorLine(e.Error())
				}
			},
			// 实时输出下载状态列表的状态
			func(pool *tp.TaskPool[*model.GranuleTask]) {
				realTimeLogger.RealTimePrint()
			})
		// 开启自动进度保存
		taskPool.EnableTaskAutoSave(taskFilePath, 20*time.Second)
		// 开始执行下载
		taskPool.Start()
		// 下载完成处理
		if !taskPool.IsInterrupt() {
			sclog.InfoLine("全部任务已下载完成！")
			sclog.WarnLine("删除进度文件...")
			_ = os.Remove(taskFilePath)
		}
	},
}

func init() {
	RootDownloadCmd.AddCommand(executeCmd)
}